﻿using STHPMS1.BLL.B_Core;
using STHPMS1.Common.C_Entity.Stmpms1;
using STHPMS1.ICore.I_BLL.Bus;
using STHPMS1.ICore.I_Entity.Info;
using STHPMS1.ICore.I_Entity.Stmpms1;
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Linq;
using STHPMS1.Common.C_Enum;
using STHPMS1.Common.C_Extension;
using System.Collections;
using Newtonsoft.Json;
using STHPMS1.ICore.I_Serv;
using STHPMS1.ICore;
using STHPMS1.BLL.B_Serv;
using STHPMS1.ICore.I_Core;
using Newtonsoft.Json.Linq;

namespace STHPMS1.BLL.B_Bus
{
    public class Dm_Origpv_RegBus : BllBase, IDm_Origpv_RegBill
    {
        public void Remove(IDm_Origpv_Reg entity)
        {
            this.Scene.Dal.Sthpms1.Delete<Dm_Origpv_Param>(p => p.Modelid == entity.Modelid);
            this.Scene.Dal.Sthpms1.DeleteById<Dm_Origpv_Reg>(entity.Modelid);            
        }

        public IModelCalculateService  GetModelSerivce(string mainSegId)
        {

            Dm_Origpv_Reg model = this.Scene.Dal.Sthpms1.Dm_Origpv_Reg.GetOrigpvRegByMainSeg<Dm_Origpv_Reg>(mainSegId);
            if (model == null) return null;
            return Load(model.Modelid);
        }

        public IModelCalculateService Load(string modelId)
        {
            IScene scene = this.Scene;
            IDm_Origpv_Reg model = scene.Dal.Sthpms1.SingleById<Dm_Origpv_Reg>(modelId);
            if (model == null) throw new Exception("model is not exitst: " + modelId);
            IEnumerable<IDm_Origpv_Param> Coefficient = scene.Dal.Sthpms1.Select<Dm_Origpv_Param>(p => p.Modelid == modelId);

            IModelCalculateService equation = null;
            if (model.Formulatype == FormulaEnum.Equation1.GetEnumValue())
            {
                equation = CreateEquation1();
            }
            else if (model.Formulatype == FormulaEnum.Equation2.GetEnumValue())
            {
                equation = CreateEquation2();
            }
            else if (model.Formulatype == FormulaEnum.Equation3.GetEnumValue())
            {
                equation = CreateEquation3();
            }
            equation.Model = model;
            equation.Coefficient = Coefficient;

            return equation;
        }


        private IEquation1Service CreateEquation1()
        {
            return new Equation1Service();
        }

        private IEquation2Service CreateEquation2()
        {
            return new Equation2Service();
        }

        private IEquation3Service CreateEquation3()
        {
            return new Equation3Service();
        }


        public IEnumerable<T> Get<T>(Expression<Func<T, bool>> predicate, IPagingInfo info)
            where T : class, IDm_Origpv_Reg
        {
            return this.Scene.Dal.Sthpms1.Select<T>(predicate, info);
        }
        
        public void Save<T>(T entity)
            where T : class ,IDm_Origpv_Reg
        {
            if((int)entity.Modeltype==ModelEnum.Original.GetEnumValue())
            {
                Dm_Oripv_Anagroup oripvGroup = GetOripvGroup(entity.Ansgroup_Id);
                if (oripvGroup == null) throw new Exception("原路面模型组不存在!");
                Dictionary<int,PerfXInfo> perfXList=GetOriginalPerfInfo<T>(entity, oripvGroup);
                if (perfXList.Count == 0) throw new Exception("此原路面模型的历史数据不存在!");
                if (perfXList.Count() < 2) throw new Exception("该模型的历史数据只有一条回归年份数值无法计算!");

                double avgX = perfXList.Keys.Average();
                double avgY = perfXList.Values.Average(p=>p.AverageY());

                double dividend = 0;//被除数
                double divisor = 0;//除数
                foreach (var item in perfXList)
                {
                    dividend += (item.Key - avgX) * (item.Value.AverageY() - avgY);
                    divisor += Math.Pow((item.Key - avgX), 2);
                }

                double a = 100;
                double b = (double)oripvGroup.Formulacoeff1;
                if (dividend != 0 && divisor != 0)
                {
                    b = dividend / divisor;
                    a = avgY - b * avgX;
                }

                Dm_Origpv_Param paraB = new Dm_Origpv_Param();
                paraB.Modelid = entity.Modelid;
                paraB.Model_Paraname = "b";
                paraB.Model_Paravalue = Math.Abs(b);

                Dm_Origpv_Param paraA = new Dm_Origpv_Param();
                paraA.Modelid = entity.Modelid;
                paraA.Model_Paraname = "a";
                paraA.Model_Paravalue = a;


                entity.Formulatype = oripvGroup.Formulatype;
                entity.Regre_Time = DateTime.Now;
                List<JObject> array=new List<JObject>();
                foreach (var itemX in perfXList.Values)
                {
                    dynamic jsonX = new JObject();
                    jsonX.X = itemX.X;
                    jsonX.YList = new JArray() as dynamic;
                    foreach (var itemY in itemX.PerfList)
	                {
                        dynamic jsonY= new JObject();
                        jsonY.Y = itemY.Key;
                        jsonY.SegList = new JArray() as dynamic;
                        jsonY.SegList.Add(itemY.Value);
                        jsonX.YList.Add(jsonY);

                    }
                    array.Add(jsonX);
                    
                }
                entity.Modelvalue = JsonConvert.SerializeObject(array.ToArray());

                //当选择直线型、指数型时，年份跨度≥1；当选择S型、多项式时，年份跨度≥2:                
                this.Scene.Dal.Sthpms1.Save(entity);
                this.Scene.Dal.Sthpms1.Save(paraA);
                this.Scene.Dal.Sthpms1.Save(paraB);
            }
            else if ((int)entity.Modeltype == ModelEnum.After.GetEnumValue())
            {
                Dm_Oripv_Formula oripvGroup = GetAfterFormula(entity.Ansgroup_Id);
                if (oripvGroup == null) throw new Exception("养护后模型组不存在!");

                if (oripvGroup.Formulatype == FormulaEnum.Equation2.GetEnumValue())
                {
                    Dm_Origpv_Param paraA = new Dm_Origpv_Param();
                    paraA.Modelid = entity.Modelid;
                    paraA.Model_Paraname = "a";
                    paraA.Model_Paravalue = oripvGroup.Formulacoeff1;

                    Dm_Origpv_Param paraB = new Dm_Origpv_Param();
                    paraB.Modelid = entity.Modelid;
                    paraB.Model_Paraname = "b";
                    paraB.Model_Paravalue = oripvGroup.Formulacoeff2;

                    entity.Formulatype = oripvGroup.Formulatype;
                    entity.Regre_Time = DateTime.Now;
                    if (this.Scene.Dal.Sthpms1.Update(entity) == 0)
                    {
                        this.Scene.Dal.Sthpms1.Insert(entity);
                    }
                    if (this.Scene.Dal.Sthpms1.Update(paraA) == 0)
                    {
                        this.Scene.Dal.Sthpms1.Insert(paraA);
                    }
                    if (this.Scene.Dal.Sthpms1.Update(paraB) == 0)
                    {
                        this.Scene.Dal.Sthpms1.Insert(paraB);
                    }

                }

                if (oripvGroup.Formulatype == FormulaEnum.Equation3.GetEnumValue())
                {
                    Dm_Origpv_Param paraA = new Dm_Origpv_Param();
                    paraA.Modelid = entity.Modelid;
                    paraA.Model_Paraname = "a";
                    paraA.Model_Paravalue = oripvGroup.Formulacoeff1;

                    Dm_Origpv_Param paraB = new Dm_Origpv_Param();
                    paraB.Modelid = entity.Modelid;
                    paraB.Model_Paraname = "b";
                    paraB.Model_Paravalue = oripvGroup.Formulacoeff2;

                    Dm_Origpv_Param paraC = new Dm_Origpv_Param();
                    paraC.Modelid = entity.Modelid;
                    paraC.Model_Paraname = "c";
                    paraC.Model_Paravalue = oripvGroup.Formulacoeff3;

                    entity.Formulatype = oripvGroup.Formulatype;
                    entity.Regre_Time = DateTime.Now;
                    if (this.Scene.Dal.Sthpms1.Update(entity) == 0)
                    {
                        this.Scene.Dal.Sthpms1.Insert(entity);
                    }
                    if (this.Scene.Dal.Sthpms1.Update(paraA) == 0)
                    {
                        this.Scene.Dal.Sthpms1.Insert(paraA);
                    }
                    if (this.Scene.Dal.Sthpms1.Update(paraB) == 0)
                    {
                        this.Scene.Dal.Sthpms1.Insert(paraB);
                    }
                    if (this.Scene.Dal.Sthpms1.Update(paraC) == 0)
                    {
                        this.Scene.Dal.Sthpms1.Insert(paraC);
                    }

                }

            }           
           
        }

        /// <summary>
        /// 获取回归年份的性能数据
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="entity"></param>
        /// <param name="oripvGroup"></param>
        /// <returns></returns>
        private Dictionary<int, PerfXInfo> GetOriginalPerfInfo<T>(T entity, Dm_Oripv_Anagroup oripvGroup) where T : class, IDm_Origpv_Reg
        {
            IEnumerable<Dm_Histperf> histperfList = GetHistperf(entity);
            IEnumerable<Dn_Divi_Result> segList = GetNewSegList();
            IEnumerable<PmstcInfo> pmstcList = GetPmstc((int)oripvGroup.Pvmttype_Dictid);


            List<PerfInfo> perfList= new List<PerfInfo>();
            foreach (Dm_Histperf itemHisperf in histperfList)
            {
                PerfInfo perf = new PerfInfo();
                Dn_Divi_Result itemSeg = segList.SingleOrDefault(p => p.Rdwayid == itemHisperf.Rdwayid && p.Mainsegid == itemHisperf.Mainsegid);//获取历史数据对应的路段信息
                if (itemSeg == null) continue;


                if (itemSeg.Lastmain_Year == null) throw new Exception("路段最近一次大修年份的数据为空 - "+itemSeg.Mainsegid);

                perf.X = itemHisperf.Perfyear - (int)itemSeg.Lastmain_Year;
                perf.Mainsegid = itemSeg.Mainsegid;
                PmstcInfo pmstc = pmstcList.FirstOrDefault<PmstcInfo>(p => p.RDWAYID == itemSeg.Rdwayid && itemSeg.Beginmp >= p.BEGINMP && itemSeg.Endmp <= p.ENDMP);//查找路段所在的路面结构
                if (pmstc != null)
                {
                    if (oripvGroup.Formulatype == FormulaEnum.Equation1.GetEnumValue())
                    {
                        //此公式计算面层厚度,如果厚度在组的厚度区间，可以进行归组
                        if (pmstc.THICK >= oripvGroup.Surf_Thickmin && pmstc.THICK <= oripvGroup.Surf_Thickmax)
                        {
                            switch (entity.Regre_Index)
                            {
                                case (int)RegressionIndicatorsEnum.PQI:
                                    {
                                        perf.Y = (double)itemHisperf.Pqiave;
                                        break;
                                    }
                                case (int)RegressionIndicatorsEnum.PCI:
                                    {
                                        perf.Y = (double)itemHisperf.Pciave;
                                        break;
                                    }
                                case (int)RegressionIndicatorsEnum.PSSI:
                                    {
                                        perf.Y = (double)itemHisperf.Pssave;
                                        break;
                                    }
                                case (int)RegressionIndicatorsEnum.RQI:
                                    {
                                        perf.Y = (double)itemHisperf.Rqiave;
                                        break;
                                    }
                                case (int)RegressionIndicatorsEnum.RDI:
                                    {
                                        perf.Y = (double)itemHisperf.Rdiave;
                                        break;
                                    }
                                case (int)RegressionIndicatorsEnum.SRI:
                                    {
                                        perf.Y = (double)itemHisperf.Sriave;
                                        break;
                                    }
                            }
                            perfList.Add(perf);
                        }
                    }
                }
            }

            ///按回归年份值分组归类
            Dictionary<int, PerfXInfo> perfXList = new Dictionary<int, PerfXInfo>();
            foreach (var item in perfList)
            {
                PerfXInfo perfX = null;
                if (!perfXList.TryGetValue(item.X, out perfX))
                {
                    perfX = new PerfXInfo();
                    perfXList.Add(item.X, perfX);
                }

                perfX.X = item.X;
                List<string> managedList=null;
                if(!perfX.PerfList.TryGetValue(item.Y,out managedList))
                {
                    managedList=new List<string>();
                    perfX.PerfList.Add(item.Y,managedList);
                }
                if (!managedList.Contains(item.Mainsegid))
                    managedList.Add(item.Mainsegid);
            }

            return perfXList;
        }

        /// <summary>
        /// 获取养护后路面分组
        /// </summary>
        /// <param name="ansgroup_Id"></param>
        /// <returns></returns>
        private Dm_Oripv_Formula GetAfterFormula(string ansgroup_Id)
        {
            return this.Scene.Dal.Sthpms1.Single<Dm_Oripv_Formula>(p => p.Ansgroup_Id == ansgroup_Id);
        }

        /// <summary>
        /// 获取路段回归年份的历史数据
        /// </summary>
        /// <param name="p"></param>
        /// <returns></returns>
        private IEnumerable<Dm_Histperf> GetHistperf(IDm_Origpv_Reg origpv)
        {
            return this.Scene.Dal.Sthpms1.Select<Dm_Histperf>(p => p.Perfyear >= origpv.Regbeginyear && p.Perfyear <= origpv.Regendyear);
        }

        /// <summary>
        /// 指定类型路面厚度
        /// </summary>
        /// <param name="ansgroup_Id"></param>
        /// <returns></returns>
        private IEnumerable<PmstcInfo> GetPmstc(int pvmttype)
        {
            return this.Scene.Dal.Sthpms1.B_Pmstc.GetPmstcThick<PmstcInfo>(pvmttype);
        }


        /// <summary>
        /// 获取模型组信息
        /// </summary>
        /// <param name="ansgroup_Id"></param>
        /// <returns></returns>
        private Dm_Oripv_Anagroup GetOripvGroup(string ansgroup_Id)
        {
            return this.Scene.Dal.Sthpms1.Single<Dm_Oripv_Anagroup>(p => p.Ansgroup_Id == ansgroup_Id);
        }

        /// <summary>
        /// 获取所有已划分好的养护路段信息(路线相同，起始桩号，终点桩号，行车方向相同，可认为同一个路段)
        /// </summary>
        /// <param name="rdwayid"></param>
        /// <returns></returns>
        private IEnumerable<Dn_Divi_Result> GetNewSegList()
        {
            IEnumerable<Dn_Divi_Result> segList = this.Scene.Dal.Sthpms1.Dn_Divi_Result.GetNewSegment<Dn_Divi_Result>(null);
            return segList;
        }
    }

    class PmstcInfo : STHPMS1.Common.C_Entity.EntityBase
    {
        public string RDWAYID { get; set; }
        public double BEGINMP { get; set; }
        public double ENDMP { get; set; }
        public double THICK { get; set; }
    }
    
    class PerfInfo
    {
        /// <summary>
        /// 回归年份值
        /// </summary>
        public int X;
        /// <summary>
        /// 指标值
        /// </summary>
        public double Y;
        /// <summary>
        /// 路段ID
        /// </summary>
        public string Mainsegid;
    }

    class PerfXInfo
    {
        /// <summary>
        /// x值
        /// </summary>
        public int X;
        /// <summary>
        /// y值，对应的路段ID
        /// </summary>
        public SortedDictionary<double, List<string>> PerfList = new SortedDictionary<double, List<string>>();

        /// <summary>
        /// 第x年所有养护路段的指标均值
        /// </summary>
        /// <returns></returns>
        public double AverageY()
        {
            return PerfList.Keys.Average();
        }
    }

}
